﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
using System.IO;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Text;
using System.Windows.Forms;
using Microsoft.Win32;

namespace MLib.Multimedia.VLC
{
	internal enum ObjectType : int
	{
		VLC_OBJECT_ROOT = (-1),
		VLC_OBJECT_VLC = (-2),
		VLC_OBJECT_MODULE = (-3),
		VLC_OBJECT_INTF = (-4),
		VLC_OBJECT_PLAYLIST = (-5),
		VLC_OBJECT_ITEM = (-6),
		VLC_OBJECT_INPUT = (-7),
		VLC_OBJECT_DECODER = (-8),
		VLC_OBJECT_VOUT = (-9),
		VLC_OBJECT_AOUT = (-10),
		VLC_OBJECT_SOUT = (-11),
		VLC_OBJECT_HTTPD = (-12),
		VLC_OBJECT_PACKETIZER = (-13),
		VLC_OBJECT_ENCODER = (-14),
		VLC_OBJECT_DIALOGS = (-15),
		VLC_OBJECT_VLM = (-16),
		VLC_OBJECT_ANNOUNCE = (-17),
		VLC_OBJECT_DEMUX = (-18),
		VLC_OBJECT_ACCESS = (-19),
		VLC_OBJECT_STREAM = (-20),
		VLC_OBJECT_OPENGL = (-21),
		VLC_OBJECT_FILTER = (-22),
		VLC_OBJECT_VOD = (-23),
		VLC_OBJECT_SPU = (-24),
		VLC_OBJECT_TLS = (-25),
		VLC_OBJECT_SD = (-26),
		VLC_OBJECT_XML = (-27),
		VLC_OBJECT_OSDMENU = (-28),
		VLC_OBJECT_STATS = (-29),
		VLC_OBJECT_HTTPD_HOST = (-30),

		VLC_OBJECT_GENERIC = (-666),
	}

	internal enum VlcError : int
	{
		Success = -0,
		NoMem = -1,
		Thread = -2,
		Timeout = -3,

		NoMod = -10,

		NoObj = -20,
		BadObj = -21,

		NoVar = -30,
		BadVar = -31,

		Exit = -255,
		Generic = -666,

		Exception = -998,
		NoInit = -999,
	};

	internal enum InputState : int
	{
		INIT_S = 0,
		OPENING_S = 1,
		BUFFERING_S = 2,
		PLAYING_S = 3,
		PAUSE_S = 4,
		END_S = 5,
		ERROR_S = 6,
	}

	internal class NativeLibVlc : IDisposable
	{
        #region private vlc enums

        enum Mode : int
        {
            Insert      = 0x01,
            Replace     = 0x02,
            Append      = 0x04,
            Go          = 0x08,
            CheckInsert = 0x10
        };

		public const Int32 EndOfPlaylist = -666;

		[Flags]
		enum ObjectSearchMode : int
		{
			FIND_PARENT         = 0x0001,
			FIND_CHILD          = 0x0002,
			FIND_ANYWHERE       = 0x0003,
			FIND_STRICT         = 0x0010,
		}

		[Flags]
		enum VarFlags : int
		{
			/** \defgroup var_flags Additive flags
			 * These flags are added to the type field of the variable. Most as a result of
			 * a __var_Change() call, but some may be added at creation time
			 * @{
			 */
			VLC_VAR_HASCHOICE     = 0x0100,
			VLC_VAR_HASMIN        = 0x0200,
			VLC_VAR_HASMAX        = 0x0400,
			VLC_VAR_HASSTEP       = 0x0800,

			VLC_VAR_ISLIST        = 0x1000,
			VLC_VAR_ISCOMMAND     = 0x2000,
			VLC_VAR_ISCONFIG      = 0x2000,

			/** Creation flag */
			VLC_VAR_DOINHERIT     = 0x8000,

			/**
			 * \defgroup var_action Variable actions
			 * These are the different actions that can be used with __var_Change().
			 * The parameters given are the meaning of the two last parameters of
			 * __var_Change() when this action is being used.
			 * @{
			 */

			/**
			 * Set the minimum value of this variable
			 * \param p_val The new minimum value
			 * \param p_val2 Unused
			 */
			VLC_VAR_SETMIN              = 0x0010,
			/**
			 * Set the maximum value of this variable
			 * \param p_val The new maximum value
			 * \param p_val2 Unused
			 */
			VLC_VAR_SETMAX              = 0x0011,
			VLC_VAR_SETSTEP             = 0x0012,
			/**
			 * Set the value of this variable without triggering any callbacks
			 * \param p_val The new value
			 * \param p_val2 Unused
			 */
			VLC_VAR_SETVALUE            = 0x0013,

			VLC_VAR_SETTEXT             = 0x0014,
			VLC_VAR_GETTEXT             = 0x0015,

			VLC_VAR_ADDCHOICE           = 0x0020,
			VLC_VAR_DELCHOICE           = 0x0021,
			VLC_VAR_CLEARCHOICES        = 0x0022,
			VLC_VAR_SETDEFAULT          = 0x0023,
			VLC_VAR_GETCHOICES          = 0x0024,
			VLC_VAR_FREECHOICES         = 0x0025,
			VLC_VAR_GETLIST             = 0x0026,
			VLC_VAR_FREELIST            = 0x0027,
			VLC_VAR_CHOICESCOUNT        = 0x0028,

			VLC_VAR_INHERITVALUE        = 0x0030,
			VLC_VAR_TRIGGER_CALLBACKS   = 0x0035,
		}

		enum playlist_command : int
		{
			PLAYLIST_PLAY = 0,      /**< No arg.                            res=can fail*/
			PLAYLIST_AUTOPLAY = 1,  /**< No arg.                            res=cant fail*/
			PLAYLIST_VIEWPLAY = 2,  /**< arg1= int, arg2= playlist_item_t*,*/
								/**  arg3 = playlist_item_t*          , res=can fail */
			PLAYLIST_ITEMPLAY = 3,  /** <arg1 = playlist_item_t *         , res=can fail */
			PLAYLIST_PAUSE = 4,     /**< No arg                             res=can fail*/
			PLAYLIST_STOP = 5,      /**< No arg                             res=can fail*/
			PLAYLIST_SKIP = 6,      /**< arg1=int,                          res=can fail*/
			PLAYLIST_GOTO = 7,      /**< arg1=int                           res=can fail */
			PLAYLIST_VIEWGOTO = 8   /**< arg1=int                           res=can fail */
		}

		enum CONFIG_ITEM : int
		{
			CONFIG_ITEM_STRING = 0x0010,
			CONFIG_ITEM_FILE = 0x0020,
			CONFIG_ITEM_MODULE = 0x0030,
			CONFIG_ITEM_INTEGER = 0x0040,
			CONFIG_ITEM_BOOL = 0x0050,
			CONFIG_ITEM_FLOAT = 0x0060,
		}

		enum input_query_e : int
		{
			/* input variable "position" */
			INPUT_GET_POSITION = 0,         /* arg1= double *       res=    */
			INPUT_SET_POSITION,         /* arg1= double         res=can fail    */

			/* input variable "length" */
			INPUT_GET_LENGTH,           /* arg1= int64_t *      res=can fail    */

			/* input variable "time" */
			INPUT_GET_TIME,             /* arg1= int64_t *      res=    */
			INPUT_SET_TIME,             /* arg1= int64_t        res=can fail    */

			/* input variable "rate" (1 is DEFAULT_RATE) */
			INPUT_GET_RATE,             /* arg1= int *          res=    */
			INPUT_SET_RATE,             /* arg1= int            res=can fail    */

			/* input variable "state" */
			INPUT_GET_STATE,            /* arg1= int *          res=    */
			INPUT_SET_STATE,            /* arg1= int            res=can fail    */

			/* input variable "audio-delay" and "sub-delay" */
			INPUT_GET_AUDIO_DELAY,      /* arg1 = int* res=can fail */
			INPUT_SET_AUDIO_DELAY,      /* arg1 = int  res=can fail */
			INPUT_GET_SPU_DELAY,        /* arg1 = int* res=can fail */
			INPUT_SET_SPU_DELAY,        /* arg1 = int  res=can fail */

			/* Meta datas */
			INPUT_ADD_INFO,   /* arg1= char* arg2= char* arg3=...     res=can fail */
			INPUT_GET_INFO,   /* arg1= char* arg2= char* arg3= char** res=can fail */
			INPUT_DEL_INFO,   /* arg1= char* arg2= char*              res=can fail */
			INPUT_SET_NAME,   /* arg1= char* res=can fail    */

			/* Input config options */
			INPUT_ADD_OPTION,      /* arg1= char * arg2= char *  res=can fail*/

			/* Input properties */
			INPUT_GET_BYTE_POSITION,     /* arg1= int64_t *       res=    */
			INPUT_SET_BYTE_SIZE,         /* arg1= int64_t *       res=    */

			/* bookmarks */
			INPUT_GET_BOOKMARKS,   /* arg1= seekpoint_t *** arg2= int * res=can fail */
			INPUT_CLEAR_BOOKMARKS, /* res=can fail */
			INPUT_ADD_BOOKMARK,    /* arg1= seekpoint_t *  res=can fail   */
			INPUT_CHANGE_BOOKMARK, /* arg1= seekpoint_t * arg2= int * res=can fail   */
			INPUT_DEL_BOOKMARK,    /* arg1= seekpoint_t *  res=can fail   */
			INPUT_SET_BOOKMARK,    /* arg1= int  res=can fail    */

			/* On the fly input slave */
			INPUT_ADD_SLAVE        /* arg1= char * */
		}

        #endregion

        #region private vlc structs
		[StructLayout(LayoutKind.Sequential)]
		struct libvlc_exception_t
		{
			public Int32 b_raised;
			public IntPtr psz_message;

			public void Init()
			{
				libvlc_exception_init(out this);
			}

			public bool WasExceptionRaised()
			{
				if(0 != libvlc_exception_raised(ref this))
				{
					libvlc_exception_clear(ref this);
					return true;
				}
				return false;
			}
		}

		[StructLayout(LayoutKind.Sequential)]
		struct libvlc_instance_t
		{
			public IntPtr p_vlc;
			public IntPtr p_playlist;
			public IntPtr p_vlm;
			public Int32 i_vlc_id;

			public libvlc_instance_t(IntPtr vlc, IntPtr playlist, int vlcHandle)
			{
				this.p_vlc = vlc;
				this.p_playlist = playlist;
				this.p_vlm = IntPtr.Zero;
				this.i_vlc_id = vlcHandle;
			}
		}

		[StructLayout(LayoutKind.Sequential)]
		struct vlc_list_t
		{
			public Int32 i_count;
			public IntPtr p_values;
			public IntPtr pi_types;
		}

        [StructLayout( LayoutKind.Explicit )]
        struct vlc_value_t
        {
            [FieldOffset( 0 )]  public Int32   i_int;
            [FieldOffset( 0 )]  public Int32   b_bool;
            [FieldOffset( 0 )][MarshalAs(UnmanagedType.R4)]  public float   f_float;
            [FieldOffset( 0 )]  public IntPtr  psz_string;
            [FieldOffset( 0 )]  public IntPtr  p_address;
            [FieldOffset( 0 )]  public IntPtr  p_object;
            [FieldOffset( 0 )]  public IntPtr  p_list;
            [FieldOffset( 0 )][MarshalAs(UnmanagedType.I8)]  public Int64   i_time;

            [FieldOffset( 0 )]  public IntPtr  psz_name;
            [FieldOffset( 4 )]  public Int32   i_object_id;
        }

		[StructLayout(LayoutKind.Sequential)]
		struct module_config_t
		{
			public CONFIG_ITEM i_type;
		}

		#endregion

		#region vlc api interop
		const int AOUT_VOLUME_MAX = 1024;
		const int VOLUME_MAX = 200;
		const int DEFAULT_CHAN = 1;
		const String Playlist_Current = "item-change";
		const String Now_Playing = "Now Playing";
		const String Meta_information = "Meta-information";
		const String Meta_title = "meta-title";
		const String Meta_author = "meta-author";
		const String Meta_artist = "meta-artist";
		const String Meta_genre = "meta-genre";
		const String Meta_description = "meta-description";
		const String Meta_url = "meta-url";
		
		[DllImport("libvlc")]
        static extern int VLC_Create();
        [DllImport("libvlc")]
		static extern VlcError VLC_Init(int iVLC, int Argc, [MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.LPStr)]string[] Argv);
        [DllImport("libvlc")]
        static extern string VLC_Version();
        [DllImport("libvlc")]
        static extern VlcError VLC_CleanUp(int iVLC);
        [DllImport("libvlc")]
        static extern VlcError VLC_Destroy(int iVLC);
        [DllImport("libvlc")]
        static extern string VLC_Error(int i_err);

		[DllImport("libvlc")]
		static extern IntPtr vlc_current_object(int i_object);
		[DllImport("libvlc")]
		static extern IntPtr __vlc_object_find(IntPtr p_vlc, ObjectType objectType, ObjectSearchMode mode);
		[DllImport("libvlc")]
		static extern void __vlc_object_release(IntPtr p_vlc);
		[DllImport("libvlc")]
		static extern VlcError __var_Set(IntPtr p_vlc, String name, vlc_value_t value);
		[DllImport("libvlc")]
		static extern VlcError __var_Get(IntPtr p_this, String name, ref vlc_value_t value);
		[DllImport("libvlc")]
		static extern VlcError __var_Change(IntPtr p_this, String name, VarFlags varFlags,
			ref vlc_value_t value, ref vlc_value_t value2);

		[DllImport("libvlc")]
		static extern VlcError __aout_VolumeGet(IntPtr p_vlc, ref Int16 volume);
		[DllImport("libvlc")]
		static extern VlcError __aout_VolumeSet(IntPtr p_vlc, Int16 volume);
		[DllImport("libvlc")]
		static extern VlcError __aout_VolumeMute(IntPtr p_vlc, IntPtr alwaysNull);

		[DllImport("libvlc")]
		static extern void libvlc_exception_init(out libvlc_exception_t p_exception);
		[DllImport("libvlc")]
		static extern Int32 libvlc_exception_raised(ref libvlc_exception_t p_exception);
		[DllImport("libvlc")]
		static extern void libvlc_exception_clear(ref libvlc_exception_t p_exception);

		[DllImport("libvlc")]
		static extern void libvlc_playlist_play(ref libvlc_instance_t libvlc, Int32 id, 
			Int32 optionsCount, IntPtr optionsAlwaysNull, ref libvlc_exception_t p_exception);
		[DllImport("libvlc")]
		static extern IntPtr libvlc_playlist_get_input(ref libvlc_instance_t libvlc, ref libvlc_exception_t p_exception);

		[DllImport("libvlc")]
		static extern int VLC_PlaylistIndex(int vlcObject);
		[DllImport("libvlc")]
		static extern int VLC_PlaylistNumberOfItems(int vlcObject);

		[DllImport("libvlc")]
		static extern void libvlc_input_free(IntPtr p_input);

		[DllImport("libvlc")]
		static extern int libvlc_video_get_width(IntPtr p_input, ref libvlc_exception_t p_exception);
		[DllImport("libvlc")]
		static extern int libvlc_video_get_height(IntPtr p_input, ref libvlc_exception_t p_exception);

		[DllImport("libvlc", CallingConvention=CallingConvention.Cdecl)]
		static extern VlcError playlist_LockControl(IntPtr p_playlist, playlist_command i_query);
		[DllImport("libvlc", CallingConvention = CallingConvention.Cdecl)]
		static extern VlcError playlist_LockControl(IntPtr p_playlist, playlist_command i_query, Int32 arg1);
		[DllImport("libvlc")]
		static extern VlcError playlist_Clear(IntPtr p_playlist);
		[DllImport("libvlc")]
		static extern VlcError playlist_AddExt(IntPtr p_playlist, String mrl, String mrlDuplicate, 
			Mode mode, Int32 pos, Int64 mtime_t, 
			[MarshalAs(UnmanagedType.LPArray, ArraySubType=UnmanagedType.LPStr)]string[] Options,
			int OptionsCount);

		[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
		delegate int VarChangedCallback(IntPtr vlc, String variable, vlc_value_t old_val, 
			vlc_value_t new_val, IntPtr param);

		[DllImport("libvlc")]
		static extern int __var_AddCallback(IntPtr vlc, String variable, VarChangedCallback cb, 
			IntPtr param);
		[DllImport("libvlc")]
		static extern int __var_DelCallback(IntPtr vlc, String variable, VarChangedCallback cb, 
			IntPtr param);
		[DllImport("libvlc", CallingConvention = CallingConvention.Cdecl, CharSet=CharSet.Ansi)]
		static extern VlcError input_Control(IntPtr input_thread_t, input_query_e i_query, String category, String name,
			ref IntPtr result);

		[DllImport("libvlc", CallingConvention = CallingConvention.Cdecl)]
		static extern void __vout_OSDMessage(IntPtr p_input, int i_channel, String message);
		[DllImport("libvlc")]
		static extern IntPtr config_FindConfig(IntPtr vlc, String name);
		[DllImport("libvlc")]
		static extern void __config_PutInt(IntPtr vlc, String name, int value);
		[DllImport("libvlc")]
		static extern void __config_PutFloat(IntPtr vlc, String name, float value);
		[DllImport("libvlc")]
		static extern void __config_PutPsz(IntPtr vlc, String name, String value);

		[DllImport("libvlc")]
		static extern int __config_GetInt(IntPtr vlc, String name);
		[DllImport("libvlc")]
		static extern float __config_GetFloat(IntPtr vlc, String name);
		[DllImport("libvlc")]
		static extern String __config_GetPsz(IntPtr vlc, String name);

		#endregion

		static NativeLibVlc()
		{
			NativeLibVlc.vlcInstallDirectory = Path.GetDirectoryName(Assembly.GetExecutingAssembly().CodeBase).Substring(6);
			//NativeLibVlc.vlcInstallDirectory = Environment.CurrentDirectory;
			//NativeLibVlc.vlcInstallDirectory = QueryVlcInstallPath();
		}

		public NativeLibVlc()
        {
        }

        #region IDisposable
        public void Dispose()
        {
            if(vlcHandle != -1)
            {
                try
                {
					if(this.gch.IsAllocated)
					{
						UnhookPlaylistChanges();
					}
					VideoOutput = null;
					VLC_CleanUp(this.vlcHandle);
                    VLC_Destroy(this.vlcHandle);
                }
                catch { }
            }
            vlcHandle = -1;
        }
        #endregion

		#region internal Vlc interop helper classes
		internal class VlcObject : IDisposable
		{
			IntPtr vlc = IntPtr.Zero;
			IntPtr subObject = IntPtr.Zero;
			bool isDisposed;

			public VlcObject(int vlcHandle, ObjectType objectType)
			{
				vlc = vlc_current_object(vlcHandle);
				if(IntPtr.Zero != vlc)
				{
					if(objectType == ObjectType.VLC_OBJECT_VLC)
					{
						subObject = vlc;
					}
					else
					{
						subObject = __vlc_object_find(vlc, objectType, ObjectSearchMode.FIND_CHILD);
					}
				}
			}

			public IntPtr Vlc { get { return this.vlc; } }
			public IntPtr SubObject { get { return this.subObject; } }

			public void Dispose()
			{
				Dispose(true);
				GC.SuppressFinalize(this);
			}

			protected virtual void Dispose(bool disposing)
			{
				if(!this.isDisposed)
				{
					this.isDisposed = true;
					if((IntPtr.Zero != subObject) && (subObject != vlc))
					{
						__vlc_object_release(subObject);
					}
					if(IntPtr.Zero != vlc)
					{
						__vlc_object_release(vlc);
					}
				}
			}

			protected bool IsDisposed { get { return this.isDisposed; } }

			~VlcObject()
			{
				Dispose(false);
			}
		}

		private class VlcPlaylistObject : VlcObject
		{
			public libvlc_instance_t libvlc;
			public libvlc_exception_t exception;

			public VlcPlaylistObject(int vlcHandle)
				: base(vlcHandle, ObjectType.VLC_OBJECT_PLAYLIST)
			{
				if(this.SubObject != IntPtr.Zero)
				{
					this.libvlc = new libvlc_instance_t(this.Vlc, this.SubObject, vlcHandle);
					this.exception.Init();
				}
			}
		}
		#endregion

		#region public Vlc interop helper functions
		public VlcObject OpenVlcObject(ObjectType objectType)
		{
			return new VlcObject(this.vlcHandle, objectType);
		}

		public int GetVlcObjectInt(ObjectType objectType, String propertyName, int errorReturn)
		{
			try
			{
				using(VlcObject vobj = new VlcObject(this.vlcHandle, objectType))
				{
					vlc_value_t intValue = new vlc_value_t();
					if((vobj.SubObject != IntPtr.Zero) &&
						(VlcError.Success == __var_Get(vobj.SubObject, propertyName, ref intValue)))
					{
						return intValue.i_int;
					}
				}
			}
			catch(Exception ex)
			{
				this.lastErrorMessage = ex.Message;
			}
			return errorReturn;
		}

		public VlcError SetVlcObjectInt(ObjectType objectType, String propertyName, int value)
		{
			try
			{
				using(VlcObject vobj = new VlcObject(this.vlcHandle, objectType))
				{
					if(vobj.SubObject != IntPtr.Zero)
					{
						vlc_value_t intValue = new vlc_value_t();
						intValue.i_int = value;
						return __var_Set(vobj.SubObject, propertyName, intValue);
					}
				}
			}
			catch(Exception ex)
			{
				this.lastErrorMessage = ex.Message;
			}
			return VlcError.NoObj;
		}

		public long GetVlcObjectLong(ObjectType objectType, String propertyName, long errorReturn)
		{
			try
			{
				using(VlcObject vobj = new VlcObject(this.vlcHandle, objectType))
				{
					vlc_value_t longValue = new vlc_value_t();
					if((vobj.SubObject != IntPtr.Zero) &&
						(VlcError.Success == __var_Get(vobj.SubObject, propertyName, ref longValue)))
					{
						return longValue.i_time;
					}
				}
			}
			catch(Exception ex)
			{
				this.lastErrorMessage = ex.Message;
			}
			return errorReturn;
		}

		public VlcError SetVlcObjectLong(ObjectType objectType, String propertyName, long value)
		{
			try
			{
				using(VlcObject vobj = new VlcObject(this.vlcHandle, objectType))
				{
					if(vobj.SubObject != IntPtr.Zero)
					{
						vlc_value_t longValue = new vlc_value_t();
						longValue.i_time = value;
						return __var_Set(vobj.SubObject, propertyName, longValue);
					}
				}
			}
			catch(Exception ex)
			{
				this.lastErrorMessage = ex.Message;
			}
			return VlcError.NoObj;
		}

		public float GetVlcObjectFloat(ObjectType objectType, String propertyName, float errorReturn)
		{
			try
			{
				using(VlcObject vobj = new VlcObject(this.vlcHandle, objectType))
				{
					vlc_value_t floatValue = new vlc_value_t();
					if((vobj.SubObject != IntPtr.Zero) &&
						(VlcError.Success == __var_Get(vobj.SubObject, propertyName, ref floatValue)))
					{
						return floatValue.f_float;
					}
				}
			}
			catch(Exception ex)
			{
				this.lastErrorMessage = ex.Message;
			}
			return errorReturn;
		}

		public VlcError SetVlcObjectFloat(ObjectType objectType, String propertyName, float value)
		{
			try
			{
				using(VlcObject vobj = new VlcObject(this.vlcHandle, objectType))
				{
					if(vobj.SubObject != IntPtr.Zero)
					{
						vlc_value_t floatValue = new vlc_value_t();
						floatValue.f_float = value;
						return __var_Set(vobj.SubObject, propertyName, floatValue);
					}
				}
			}
			catch(Exception ex)
			{
				this.lastErrorMessage = ex.Message;
			}
			return VlcError.NoObj;
		}

		public String GetVlcObjectString(ObjectType objectType, String propertyName, String errorReturn)
		{
			try
			{
				using(VlcObject vobj = new VlcObject(this.vlcHandle, objectType))
				{
					vlc_value_t stringValue = new vlc_value_t();
					if((vobj.SubObject != IntPtr.Zero) &&
						(VlcError.Success == __var_Get(vobj.SubObject, propertyName, ref stringValue)))
					{
						return Marshal.PtrToStringAnsi(stringValue.psz_string);
					}
				}
			}
			catch(Exception ex)
			{
				this.lastErrorMessage = ex.Message;
			}
			return errorReturn;
		}

		public VlcError SetVlcObjectString(ObjectType objectType, String propertyName, String value)
		{
			try
			{
				using(VlcObject vobj = new VlcObject(this.vlcHandle, objectType))
				{
					if(vobj.SubObject != IntPtr.Zero)
					{
						vlc_value_t stringValue = new vlc_value_t();
						IntPtr valuePtr = Marshal.StringToCoTaskMemAnsi(value);
						stringValue.psz_string = valuePtr;
						VlcError ret = __var_Set(vobj.SubObject, propertyName, stringValue);
						Marshal.ZeroFreeCoTaskMemAnsi(valuePtr);
						return ret;
					}
				}
			}
			catch(Exception ex)
			{
				this.lastErrorMessage = ex.Message;
			}
			return VlcError.NoObj;
		}

		public VlcError GetVlcVariableChoiceList(ObjectType objectType, String propertyName,
			out int[] choiceIds, out String[] choiceText)
		{
			try
			{
				using(VlcObject vobj = new VlcObject(this.vlcHandle, objectType))
				{
					if(vobj.SubObject != IntPtr.Zero)
					{
						vlc_value_t idValues = new vlc_value_t();
						vlc_value_t textValues = new vlc_value_t();
						if(VlcError.Success == __var_Change(vobj.SubObject, propertyName,
							VarFlags.VLC_VAR_GETLIST, ref idValues, ref textValues))
						{
							try
							{
								vlc_list_t idList = (vlc_list_t)Marshal.PtrToStructure(
									idValues.p_list, typeof(vlc_list_t));
								vlc_list_t textList = (vlc_list_t)Marshal.PtrToStructure(
									textValues.p_list, typeof(vlc_list_t));

								int choiceCount = idList.i_count;
								choiceIds = new Int32[choiceCount];
								choiceText = new String[choiceCount];

								for(int index = 0; index < choiceCount; index++)
								{
									IntPtr idPtr = new IntPtr(idList.p_values.ToInt32() +
										index * Marshal.SizeOf(typeof(vlc_value_t)));
									vlc_value_t idValue = (vlc_value_t)Marshal.PtrToStructure(
										idPtr, typeof(vlc_value_t));
									choiceIds[index] = idValue.i_int;

									IntPtr textPtr = new IntPtr(textList.p_values.ToInt32() +
										index * Marshal.SizeOf(typeof(vlc_value_t)));
									vlc_value_t textValue = (vlc_value_t)Marshal.PtrToStructure(
										textPtr, typeof(vlc_value_t));
									choiceText[index] = Marshal.PtrToStringAnsi(textValue.psz_string);
								}
								return VlcError.Success;
							}
							finally
							{
								__var_Change(vobj.SubObject, propertyName, VarFlags.VLC_VAR_FREELIST,
									ref idValues, ref textValues);
							}
						}
					}
				}
			}
			catch(Exception ex)
			{
				this.lastErrorMessage = ex.Message;
			}

			choiceIds = new int[0];
			choiceText = new string[0];
			return VlcError.NoObj;
		}

		public VlcError GetVlcVariableChoiceList(ObjectType objectType, String propertyName,
			out String[] choices, out String[] choiceText)
		{
			try
			{
				using(VlcObject vobj = new VlcObject(this.vlcHandle, objectType))
				{
					if(vobj.SubObject != IntPtr.Zero)
					{
						vlc_value_t idValues = new vlc_value_t();
						vlc_value_t textValues = new vlc_value_t();
						if(VlcError.Success == __var_Change(vobj.SubObject, propertyName,
							VarFlags.VLC_VAR_GETLIST, ref idValues, ref textValues))
						{
							try
							{
								vlc_list_t idList = (vlc_list_t)Marshal.PtrToStructure(
									idValues.p_list, typeof(vlc_list_t));
								vlc_list_t textList = (vlc_list_t)Marshal.PtrToStructure(
									textValues.p_list, typeof(vlc_list_t));

								int choiceCount = idList.i_count;
								List<String> choiceList = new List<string>(choiceCount);
								List<String> choiceTextList = new List<string>(choiceCount);
								Dictionary<String, int> choiceDict = new Dictionary<string, int>(choiceCount);
								for(int index = 0; index < choiceCount; index++)
								{
									IntPtr idPtr = new IntPtr(idList.p_values.ToInt32() +
										index * Marshal.SizeOf(typeof(vlc_value_t)));
									vlc_value_t idValue = (vlc_value_t)Marshal.PtrToStructure(
										idPtr, typeof(vlc_value_t));
									String choice = Marshal.PtrToStringAnsi(idValue.psz_name);
									choiceList.Add(choice);
									if(choiceDict.ContainsKey(choice))
									{
										choiceDict[choice] = choiceDict[choice] + 1;
									}
									else
									{
										choiceDict[choice] = 1;
									}

									IntPtr textPtr = new IntPtr(textList.p_values.ToInt32() +
										index * Marshal.SizeOf(typeof(vlc_value_t)));
									vlc_value_t textValue = (vlc_value_t)Marshal.PtrToStructure(
										textPtr, typeof(vlc_value_t));
									choiceTextList.Add(Marshal.PtrToStringAnsi(textValue.psz_string));
								}

								int listIndex = 0;
								for(int index = 0; index < choiceCount; index++)
								{
									String choice = choiceList[listIndex];
									if((choiceDict[choice] > 1) && (choiceTextList[listIndex] == null))
									{
										choiceList.RemoveAt(listIndex);
										choiceTextList.RemoveAt(listIndex);
										choiceDict[choice] = choiceDict[choice] - 1;
									}
									else
									{
										listIndex++;
									}
								}
								for(int index = 0; index < choiceList.Count; index++)
								{
									if(choiceTextList[index] == null)
									{
										choiceTextList[index] = choiceList[index];
									}
								}

								choices = choiceList.ToArray();
								choiceText = choiceTextList.ToArray();
								return VlcError.Success;
							}
							finally
							{
								__var_Change(vobj.SubObject, propertyName, VarFlags.VLC_VAR_FREELIST,
									ref idValues, ref textValues);
							}
						}
					}
				}
			}
			catch(Exception ex)
			{
				this.lastErrorMessage = ex.Message;
			}

			choices = new string[0];
			choiceText = new string[0];
			return VlcError.NoObj;
		}
		#endregion

        #region public Properties
        public static string VlcInstallDir
        {
            get{ return vlcInstallDirectory; }
            set{ vlcInstallDirectory = value; }
        }

        public bool IsInitialized
        {
            get{return (vlcHandle != -1);}
        }

        public Control VideoOutput
        {
            get { return outputWindow; }
            set
            {
				if(value == null)
				{
					if(outputWindow != null)
					{
						outputWindow = null;
						if(vlcHandle != -1)
						{
							SetVlcObjectInt(ObjectType.VLC_OBJECT_VLC, "drawable", 0);
						}
					}
				}
				else
				{
					outputWindow = value;
					if(vlcHandle != -1)
					{
						SetVlcObjectInt(ObjectType.VLC_OBJECT_VLC, "drawable", outputWindow.Handle.ToInt32());
					}
				}
            }
        }

        public string LastError
        {
            get{return lastErrorMessage;}
        }

		public event MetaDataEventHandler NowPlaying;

		protected virtual void OnNowPlaying(MetaDataUpdateEventArgs args)
		{
			if(this.NowPlaying != null)
			{
				this.NowPlaying(this, args);
			}
		}

		protected delegate void HandleNowPlaying(MetaDataUpdateEventArgs args);

		protected virtual void VlcNowPlayingChanged(String newText)
		{
			// switch out of the Vlc thread to our User Interface thread if possible
			if(this.VideoOutput != null)
			{
				this.VideoOutput.BeginInvoke(new HandleNowPlaying(OnNowPlaying),
					new MetaDataUpdateEventArgs(MetaData.NowPlaying, newText));
			}
			else
			{
				OnNowPlaying(new MetaDataUpdateEventArgs(MetaData.NowPlaying, newText));
			}
		}

        public int Length
        {
            get
            {
				if(this.artificialLength != 0)
				{
					return this.artificialLength;
				}
				else
				{
					return Convert.ToInt32(GetVlcObjectLong(ObjectType.VLC_OBJECT_INPUT, "length", 0) / 1000000L);
				}
            }
        }

		public void SetArtificialLength(int newLength)
		{
			this.artificialLength = newLength;
		}

        public int Time
        {
            get
            {
				int time = Convert.ToInt32(GetVlcObjectLong(ObjectType.VLC_OBJECT_INPUT, "time", 0) / 1000000L);
				if((time == 0) && (this.artificialLength != 0))
				{
					time = Convert.ToInt32(Position * this.artificialLength + .5d);
				}
				if(this.timeScaling != 0.0d)
				{
					time = Convert.ToInt32(time / this.timeScaling);
				}
				return time;
            }
			set
			{
				if(this.artificialLength != 0)
				{
					float position = Convert.ToSingle(value) / Convert.ToSingle(this.artificialLength);
					if(this.timeScaling != 0.0d)
					{
						position = Convert.ToSingle(position * this.timeScaling);
					}
					Debug.WriteLine(String.Format("Set Position {0}", position));
					SetVlcObjectFloat(ObjectType.VLC_OBJECT_INPUT, "position", position);
				}
				else
				{
					long time = Convert.ToInt64(value) * 1000000L;
					if(this.timeScaling != 0.0d)
					{
						time = Convert.ToInt64(time * this.timeScaling);
					}
					Debug.WriteLine(String.Format("Set Time {0}", time));
					SetVlcObjectLong(ObjectType.VLC_OBJECT_INPUT, "time", time);
				}
			}
        }

		public double TimeScaling
		{
			get { return this.timeScaling; }
			set { this.timeScaling = value; }
		}

        public double Position
        {
            get
            {
				double position = GetVlcObjectFloat(ObjectType.VLC_OBJECT_INPUT, "position", 0.0f);
				if(this.timeScaling != 0.0d)
				{
					position = position / this.timeScaling;
				}
				return position;
            }
			set
			{
				double position = value;
				if(this.timeScaling != 0.0d)
				{
					position = position * this.timeScaling;
				}
				Debug.WriteLine(String.Format("Set Position {0}", position));
				SetVlcObjectFloat(ObjectType.VLC_OBJECT_INPUT, "position", Convert.ToSingle(position));
			}
        }

        public int Volume
        {
            get
            {
				IntPtr vlc = vlc_current_object(vlcHandle);
				if(IntPtr.Zero != vlc)
				{
					try
					{
						Int16 aoutVol = 0;
						if(__aout_VolumeGet(vlc, ref aoutVol) == VlcError.Success)
						{
							return (aoutVol * VOLUME_MAX + AOUT_VOLUME_MAX / 2) / AOUT_VOLUME_MAX;
						}
					}
					catch(Exception)
					{
					}
					finally
					{
						__vlc_object_release(vlc);
					}
				}
				return 0;
            }
            set
            {
				IntPtr vlc = vlc_current_object(vlcHandle);
				if(IntPtr.Zero != vlc)
				{
					try
					{
						Int16 aoutVol = Convert.ToInt16((value * AOUT_VOLUME_MAX + VOLUME_MAX / 2) / VOLUME_MAX);
						__aout_VolumeSet(vlc, aoutVol);
					}
					catch(Exception)
					{
					}
					finally
					{
						__vlc_object_release(vlc);
					}
				}
            }
        }

        public bool Fullscreen
        {
            get
            {
				int isFullScreen = GetVlcObjectInt(ObjectType.VLC_OBJECT_VLC, "fullscreen", -666);
				if((isFullScreen != -666) && (isFullScreen != 0))
				{
					return true;
				}
                return false;
               
            }
            set
            {
				SetVlcObjectInt(ObjectType.VLC_OBJECT_VLC, "fullscreen", value ? 1 : 0);
            }
        }

		public int PlaylistIndex
		{
			get
			{
				try
				{
					return VLC_PlaylistIndex(this.vlcHandle);
				}
				catch(Exception ex)
				{
					this.lastErrorMessage = ex.Message;
					return -1;
				}
			}
		}

		public int PlaylistCount
		{
			get
			{
				try
				{
					return VLC_PlaylistNumberOfItems(this.vlcHandle);
				}
				catch(Exception ex)
				{
					this.lastErrorMessage = ex.Message;
					return -1;
				}
			}
		}

        #endregion

        #region public Methods
        public bool Initialize()
        {
            // check if already initializes
            if(vlcHandle != -1)
                return true;

			string oldDir = Environment.CurrentDirectory;
			// try init
			try
			{
				// create instance
				Environment.CurrentDirectory = NativeLibVlc.vlcInstallDirectory;
				this.vlcHandle = VLC_Create();

				if(this.vlcHandle < 0)
				{
					lastErrorMessage = "Failed to create VLC instance";
					return false;
				}

				string[] initOptions = {	NativeLibVlc.vlcInstallDirectory,
											":no-one-instance",
											":no-loop",
											":no-drop-late-frames",
											":disable-screensaver",
											":vout=vout_directx",
											"--plugin-path=" + NativeLibVlc.vlcInstallDirectory + @"\plugins",
					};

				// init libvlc
				VlcError errVlcLib = VLC_Init(vlcHandle, initOptions.Length, initOptions);
				if(errVlcLib != VlcError.Success)
				{
					VLC_Destroy(vlcHandle);
					lastErrorMessage = "Failed to initialise VLC";
					this.vlcHandle = -1;
					return false;
				}
			}
			catch
			{
				lastErrorMessage = "Could not find libvlc";
				return false;
			}
			finally
			{
				Environment.CurrentDirectory = oldDir;
			}

            // check output window
            if(outputWindow != null)
            {
				SetVlcObjectInt(ObjectType.VLC_OBJECT_VLC, "drawable", outputWindow.Handle.ToInt32());
			}

            return true;
        }

		// there's an overhead to supporting Vlc events, a memory leak, so we need to have them off by default
		public bool ProducingEvents
		{
			get { return this.gch.IsAllocated; }
			set
			{
				if(value)
				{
					if(!this.gch.IsAllocated)
					{
						HookPlaylistChanges();
					}
				}
				else
				{
					if(this.gch.IsAllocated)
					{
						UnhookPlaylistChanges();
					}
				}
			}
		}

		private static String AdjustFilterString(String current, String filter, bool add)
		{
			String newFilter = null;
			int findFilter = current.IndexOf(filter);
			if(findFilter == -1)
			{
				if(add)
				{
					if(current.Length == 0)
					{
						newFilter = filter;
					}
					else
					{
						newFilter = String.Format("{0}:{1}", current, filter);
					}
				}
			}
			else
			{
				if(!add)
				{
					int colonAfterAdjust = current.IndexOf(':', findFilter + 1);
					if(findFilter == 0)
					{
						if(colonAfterAdjust == -1)
						{
							newFilter = String.Empty;
						}
						else
						{
							newFilter = current.Substring(colonAfterAdjust + 1);
						}
					}
					else
					{
						if(colonAfterAdjust == -1)
						{
							newFilter = current.Substring(0, findFilter - 1);
						}
						else
						{
							newFilter = current.Substring(0, findFilter - 1) +
								current.Substring(colonAfterAdjust);
						}
					}
				}
			}
			return newFilter;
		}

		const String VideoFilterList = "vout-filter";
		const String AdjustFilter = "adjust";

		public bool AllowVideoAdjustments
		{
			get
			{
				String voutFilter = GetVlcObjectString(ObjectType.VLC_OBJECT_VOUT, VideoFilterList, null);
				if(voutFilter == null)
				{
					GetConfigVariable(VideoFilterList, out voutFilter);
				}
				if(voutFilter != null)
				{
					return voutFilter.IndexOf(AdjustFilter) != -1;
				}
				else
				{
					return false;
				}
			}
			set
			{
				bool useConfig = false;
				String voutFilter = GetVlcObjectString(ObjectType.VLC_OBJECT_VOUT, VideoFilterList, null);
				if(voutFilter == null)
				{
					using(VlcObject vlc = new VlcObject(this.vlcHandle, ObjectType.VLC_OBJECT_VLC))
					{
						GetConfigVariable(VideoFilterList, out voutFilter);
						useConfig = true;
					}
				}
				if(voutFilter != null)
				{
					String newVoutFilter = AdjustFilterString(voutFilter, AdjustFilter, value);
					if(newVoutFilter != null)
					{
						if(useConfig)
						{
							SetConfigVariable(VideoFilterList, newVoutFilter);
						}
						else
						{
							SetVlcObjectString(ObjectType.VLC_OBJECT_VOUT, VideoFilterList, newVoutFilter);
						}
					}
				}
			}
		}

		public VlcError AddTarget(string target, ref int itemId)
        {
			try
			{
				using(VlcObject vobj = new VlcObject(this.vlcHandle, ObjectType.VLC_OBJECT_PLAYLIST))
				{
					if(vobj.SubObject != IntPtr.Zero)
					{
						VlcError enmErr = playlist_AddExt(vobj.SubObject, target, target, Mode.Append, 
							EndOfPlaylist, -1L, null, 0);
						if(enmErr >= VlcError.Success)
						{
							itemId = (int)enmErr;
						}
						return enmErr;
					}
					else
					{
						return VlcError.NoObj;
					}
				}
			}
			catch(Exception ex)
			{
				this.lastErrorMessage = ex.Message;
				return VlcError.Exception;
			}
		}

		public VlcError AddTarget(string target, string[] options, ref int itemId)
        {
            int optionsCount = 0;
			if(options != null)
			{
				optionsCount = options.Length;
			}

			try
			{
				using(VlcObject vobj = new VlcObject(this.vlcHandle, ObjectType.VLC_OBJECT_PLAYLIST))
				{
					if(vobj.SubObject != IntPtr.Zero)
					{
						VlcError enmErr = playlist_AddExt(vobj.SubObject, target, target, Mode.Append,
							EndOfPlaylist, -1L, options, optionsCount);
						if(enmErr >= VlcError.Success)
						{
							itemId = (int)enmErr;
						}
						return enmErr;
					}
					else
					{
						return VlcError.NoObj;
					}
				}
			}
			catch(Exception ex)
			{
				this.lastErrorMessage = ex.Message;
				return VlcError.Exception;
			}
        }

		public Size VideoSize
		{
			get
			{
				try
				{
					using(VlcPlaylistObject vpobj = new VlcPlaylistObject(this.vlcHandle))
					{
						if(vpobj.SubObject != IntPtr.Zero)
						{
							IntPtr p_input = libvlc_playlist_get_input(ref vpobj.libvlc, ref vpobj.exception);
							if(vpobj.exception.WasExceptionRaised())
							{
								this.lastErrorMessage = Marshal.PtrToStringAnsi(vpobj.exception.psz_message);
							}
							else
							{
								try
								{
									int width = libvlc_video_get_width(p_input, ref vpobj.exception);
									if(!vpobj.exception.WasExceptionRaised())
									{
										int height = libvlc_video_get_height(p_input, ref vpobj.exception);
										if(!vpobj.exception.WasExceptionRaised())
										{
											return new Size(width, height);
										}
									}
								}
								finally
								{
									libvlc_input_free(p_input);
								}
							}
						}
					}
				}
				catch(Exception ex)
				{
					this.lastErrorMessage = ex.Message;
				}
				return new Size();
			}
		}

		public VlcError Play(int itemId)
		{
			try
			{
				this.artificialLength = 0;
				this.timeScaling = 0.0d;
				using(VlcPlaylistObject vpobj = new VlcPlaylistObject(this.vlcHandle))
				{
					if(vpobj.SubObject != IntPtr.Zero)
					{
						libvlc_playlist_play(ref vpobj.libvlc, itemId, 0, IntPtr.Zero, 
							ref vpobj.exception);
						if(vpobj.exception.WasExceptionRaised())
						{
							return VlcError.Generic;
						}
						return VlcError.Success;
					}
				}
			}
			catch(Exception ex)
			{
				this.lastErrorMessage = ex.Message;
				return VlcError.Exception;
			}
			return VlcError.NoObj;
		}

        public VlcError Play()
        {
			try
			{
				using(VlcObject vobj = new VlcObject(this.vlcHandle, ObjectType.VLC_OBJECT_PLAYLIST))
				{
					if(vobj.SubObject != IntPtr.Zero)
					{
						return playlist_LockControl(vobj.SubObject, playlist_command.PLAYLIST_PLAY);
					}
					else
					{
						return VlcError.NoObj;
					}
				}
			}
			catch(Exception ex)
			{
				this.lastErrorMessage = ex.Message;
				return VlcError.Exception;
			}
        }

        public VlcError Pause()
        {
			try
			{
				using(VlcObject vobj = new VlcObject(this.vlcHandle, ObjectType.VLC_OBJECT_PLAYLIST))
				{
					if(vobj.SubObject != IntPtr.Zero)
					{
						return playlist_LockControl(vobj.SubObject, playlist_command.PLAYLIST_PAUSE);
					}
					else
					{
						return VlcError.NoObj;
					}
				}
			}
			catch(Exception ex)
			{
				this.lastErrorMessage = ex.Message;
				return VlcError.Exception;
			}
        }

        public VlcError Stop()
        {
			try
			{
				using(VlcObject vobj = new VlcObject(this.vlcHandle, ObjectType.VLC_OBJECT_PLAYLIST))
				{
					if(vobj.SubObject != IntPtr.Zero)
					{
						return playlist_LockControl(vobj.SubObject, playlist_command.PLAYLIST_STOP);
					}
					else
					{
						return VlcError.NoObj;
					}
				}
			}
			catch(Exception ex)
			{
				this.lastErrorMessage = ex.Message;
				return VlcError.Exception;
			}
		}

        public VlcError PlaylistClear()
        {
			try
			{
				using(VlcObject vobj = new VlcObject(this.vlcHandle, ObjectType.VLC_OBJECT_PLAYLIST))
				{
					if(vobj.SubObject != IntPtr.Zero)
					{
						return playlist_Clear(vobj.SubObject);
					}
					else
					{
						return VlcError.NoObj;
					}
				}
			}
			catch(Exception ex)
			{
				this.lastErrorMessage = ex.Message;
				return VlcError.Exception;
			}
        }

        public VlcError ToggleVolumeMute()
        {
			IntPtr vlc = vlc_current_object(vlcHandle);
			if(IntPtr.Zero != vlc)
			{
				try
				{
					return __aout_VolumeMute(vlc, IntPtr.Zero);
				}
				catch(Exception)
				{
				}
				finally
				{
					__vlc_object_release(vlc);
				}
			}
            return VlcError.NoObj;
        }

        public VlcError PressKey(string strKey)
        {
			int key = GetVlcObjectInt(ObjectType.VLC_OBJECT_VLC, strKey, -666);
			if(key != -666)
			{
				return SetVlcObjectInt(ObjectType.VLC_OBJECT_VLC, "key-pressed", key);
			}
			return VlcError.NoVar;
		}

		public VlcError ShowMessage(String message)
		{
			try
			{
				using(VlcObject vobj = new VlcObject(this.vlcHandle, ObjectType.VLC_OBJECT_INPUT))
				{
					if(vobj.SubObject != IntPtr.Zero)
					{
						__vout_OSDMessage(vobj.SubObject, DEFAULT_CHAN, message);
						return VlcError.Success;
					}
					else
					{
						return VlcError.NoObj;
					}
				}
			}
			catch(Exception ex)
			{
				this.lastErrorMessage = ex.Message;
				return VlcError.Exception;
			}
		}

		public VlcError GetConfigVariable(String name, out String value)
		{
			value = null;
			using(VlcObject vlc = new VlcObject(this.vlcHandle, ObjectType.VLC_OBJECT_VLC))
			{
				if(IntPtr.Zero == vlc.SubObject)
				{
					return VlcError.NoObj;
				}

				IntPtr p_item = config_FindConfig(vlc.SubObject, name);
				if(IntPtr.Zero == p_item)
				{
					return VlcError.NoVar;
				}

				try
				{
					module_config_t mod = (module_config_t)Marshal.PtrToStructure(p_item, typeof(module_config_t));
					switch(mod.i_type)
					{
					case CONFIG_ITEM.CONFIG_ITEM_BOOL:
						{
							bool result = (__config_GetInt(vlc.SubObject, name) == 0);
							value = result.ToString();
						}
						break;
					case CONFIG_ITEM.CONFIG_ITEM_INTEGER:
						{
							int intResult = __config_GetInt(vlc.SubObject, name);
							value = intResult.ToString();
						}
						break;
					case CONFIG_ITEM.CONFIG_ITEM_FLOAT:
						{
							float floatResult = __config_GetFloat(vlc.SubObject, name);
							value = floatResult.ToString();
						}
						break;
					case CONFIG_ITEM.CONFIG_ITEM_STRING:
						value = __config_GetPsz(vlc.SubObject, name);
						break;
					default:
						return VlcError.BadVar;
					}
				}
				catch(Exception e)
				{
					this.lastErrorMessage = e.Message;
					return VlcError.Exception;
				}
			}
			return VlcError.Success;
		}

		public VlcError SetConfigVariable(String name, String value)
		{
			using(VlcObject vlc = new VlcObject(this.vlcHandle, ObjectType.VLC_OBJECT_VLC))
			{
				if(IntPtr.Zero == vlc.SubObject)
				{
					return VlcError.NoObj;
				}

				IntPtr p_item = config_FindConfig(vlc.SubObject, name);
				if(IntPtr.Zero == p_item)
				{
					return VlcError.NoVar;
				}
				try
				{
					module_config_t mod = (module_config_t)Marshal.PtrToStructure(p_item, typeof(module_config_t));
					switch(mod.i_type)
					{
					case CONFIG_ITEM.CONFIG_ITEM_BOOL:
						{
							bool boolResult;
							if(Boolean.TryParse(value, out boolResult))
							{
								__config_PutInt(vlc.SubObject, name, boolResult ? 1 : 0);
							}
							else
							{
								return VlcError.BadVar;
							}
						}
						break;
					case CONFIG_ITEM.CONFIG_ITEM_INTEGER:
						{
							int intResult;
							if(Int32.TryParse(value, out intResult))
							{
								__config_PutInt(vlc.SubObject, name, intResult);
							}
							else
							{
								return VlcError.BadVar;
							}
						}
						break;
					case CONFIG_ITEM.CONFIG_ITEM_FLOAT:
						{
							float floatResult;
							if(Single.TryParse(value, out floatResult))
							{
								__config_PutFloat(vlc.SubObject, name, floatResult);
							}
							else
							{
								return VlcError.BadVar;
							}
						}
						break;
					case CONFIG_ITEM.CONFIG_ITEM_STRING:
						__config_PutPsz(vlc.SubObject, name, value);
						break;
					default:
						return VlcError.BadVar;
					}
				}
				catch(Exception e)
				{
					this.lastErrorMessage = e.Message;
					return VlcError.Exception;
				}
			}
			return VlcError.Success;
		}

        #endregion

		#region private members, properties and methods

		private static string vlcInstallDirectory = "";
		private int vlcHandle = -1;
		private Control outputWindow = null;
		private string lastErrorMessage = "";
		private int artificialLength;
		private double timeScaling;
		private GCHandle gch;
		private VarChangedCallback currentTrackCallback;
		private string previousNowPlaying = String.Empty;

        /// -------------------------------------------------------------------
        /// <summary>
        /// Method name      :   QueryVlcInstallPath
        /// Author         :   Odysee
        ///   Date         :   10.11.2006
        /// </summary>
        /// -------------------------------------------------------------------
        private static string QueryVlcInstallPath()
        {
            // open registry
            RegistryKey regkeyVlcInstallPathKey = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\VideoLAN\VLC");
            if(regkeyVlcInstallPathKey == null)
                return "";
            return (string)regkeyVlcInstallPathKey.GetValue("InstallDir","");
        }

		private void HookPlaylistChanges()
		{
			using(VlcObject vlc = new VlcObject(this.vlcHandle, ObjectType.VLC_OBJECT_PLAYLIST))
			{
				if(vlc.SubObject != IntPtr.Zero)
				{
					this.gch = GCHandle.Alloc(this);
					this.currentTrackCallback = new VarChangedCallback(CurrentTrackChanged);

					int isSet = __var_AddCallback(vlc.SubObject, Playlist_Current,
						this.currentTrackCallback, (IntPtr)this.gch);
					//Debug.WriteLine("__var_AddCallback playlistObject = " + isSet.ToString());
				}
			}
		}

		private void UnhookPlaylistChanges()
		{
			using(VlcObject vlc = new VlcObject(this.vlcHandle, ObjectType.VLC_OBJECT_PLAYLIST))
			{
				if(vlc.SubObject != IntPtr.Zero)
				{
					__var_DelCallback(vlc.SubObject, Playlist_Current,
						this.currentTrackCallback, (IntPtr)this.gch);
				}
			}
			this.gch.Free();
		}

		private static int CurrentTrackChanged(IntPtr vlc, String variable, vlc_value_t old_val,
			vlc_value_t new_val, IntPtr param)
		{
			//Debug.WriteLine("CurrentTrackChanged: " + new_val.i_int.ToString());
			GCHandle gch = (GCHandle)param;
			NativeLibVlc thisVlc = (NativeLibVlc)gch.Target;
			try
			{
				using(VlcObject vobj = new VlcObject(thisVlc.vlcHandle, ObjectType.VLC_OBJECT_INPUT))
				{
					if(vobj.SubObject != IntPtr.Zero)
					{
						IntPtr resultString = IntPtr.Zero;
						input_Control(vobj.SubObject, input_query_e.INPUT_GET_INFO,
							Meta_information, Now_Playing, ref resultString);
						String nowPlaying = Marshal.PtrToStringAnsi(resultString);
						if(nowPlaying != thisVlc.previousNowPlaying)
						{
							thisVlc.previousNowPlaying = nowPlaying;
							Debug.WriteLine(String.Format("nowPlaying: {0}", nowPlaying));
							thisVlc.VlcNowPlayingChanged(nowPlaying);
						}
					}
				}
			}
			catch(Exception ex)
			{
				thisVlc.lastErrorMessage = ex.Message;
			}
			return (int)VlcError.Success;
		}

        #endregion
	}

	/* future features

    add_bool( "brightness-threshold", 0, NULL,
              THRES_TEXT, THRES_LONGTEXT, VLC_FALSE );
	 */
}